package com.ultisense.gassensor;

import android.bluetooth.BluetoothGatt;
import android.bluetooth.BluetoothGattCallback;
import android.bluetooth.BluetoothGattCharacteristic;
import android.bluetooth.BluetoothGattDescriptor;
import android.bluetooth.BluetoothGattService;
import android.bluetooth.BluetoothProfile;
import android.content.Context;
import android.graphics.Color;
import android.os.Build;
import android.support.v4.app.Fragment;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothManager;
import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.text.Spannable;
import android.text.SpannableString;
import android.text.style.ForegroundColorSpan;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.CheckBox;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.EditText;
import android.widget.Button;

import java.io.IOException;
import java.math.BigInteger;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.Random;
import java.util.UUID;

import com.github.mikephil.charting.charts.LineChart;
import com.github.mikephil.charting.components.AxisBase;
import com.github.mikephil.charting.components.LimitLine;
import com.github.mikephil.charting.components.XAxis;
import com.github.mikephil.charting.components.YAxis;
import com.github.mikephil.charting.data.Entry;
import com.github.mikephil.charting.data.LineData;
import com.github.mikephil.charting.data.LineDataSet;
import com.github.mikephil.charting.formatter.IAxisValueFormatter;
import com.github.mikephil.charting.interfaces.datasets.ILineDataSet;

public class BtFragment extends Fragment {
    private TextView myLabel;
    private EditText myRecord;
    private CheckBox myCheckbox;
    private DataProcessor mData;
    private static BluetoothAdapter mBluetoothAdapter;
    private BluetoothManager mBluetoothManager;
    private Handler mHandler;
    private BluetoothDevice mmDevice;
    private static BluetoothGatt mBluetoothGatt;
    BluetoothGattCharacteristic mCharacteristic;
    BluetoothGattDescriptor mDescriptor;
    private Thread workerThread;
    private int graphCounter;
    private int cycleCounter;
    private float baseline;
    private int sensorNum;
    private float maxLevel;
    private float criticalLevel;
    private boolean findDevice;
    volatile boolean stopWorker;

    private ArrayList<Entry> entries;
    private ArrayList<Entry> entries2;
    private ArrayList<Entry> entries3;
    private LineDataSet set1;
    private LineDataSet set2;
    private LineDataSet set3;
    private ArrayList<ILineDataSet> dataSets;
    private LineData mLineData;
    private LineChart mGraph;
    private ProgressBar mProgress;


    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);


        //Log.i("test activity","onCreate is called");
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        //Log.i("test activity","onCreateView is called");
        return inflater.inflate(R.layout.fragment_bt, container, false);
    }

    @Override
    public void onStart() {
        super.onStart();
        Button openButton = (Button) getActivity().findViewById(R.id.open);
        Button connectButton = (Button) getActivity().findViewById(R.id.connect);
        Button closeButton = (Button) getActivity().findViewById(R.id.close);
        //Log.i("test activity","onStart is called");

        myLabel = (TextView) getActivity().findViewById(R.id.label);
        myRecord = (EditText) getActivity().findViewById(R.id.dataStream);
        myCheckbox = (CheckBox) getActivity().findViewById(R.id.checkBox);
        mProgress = (ProgressBar) getActivity().findViewById(R.id.progressBar);
        mGraph = (LineChart) getActivity().findViewById(R.id.graph);
        mData = new DataProcessor();
        mHandler = new Handler();
        mProgress.setMax(10000);
        mProgress.setProgress(10000);
        cycleCounter = 0;
        graphCounter = 0;
        baseline = 0;

        sensorNum = 5;
        maxLevel = 200f;
        criticalLevel = 160f;

        //Open Button
        openButton.setOnClickListener(new View.OnClickListener() {
            public void onClick(View v) {
                findBT();
                /*new Thread(new Runnable() {
                    @Override
                    public void run() {
                        getActivity().runOnUiThread(new Runnable() {
                            @Override
                            public void run() {
                                for(int i=0; i<5;++i){
                                    updateGraph((new Random().nextFloat())*200);
                                }
                            }
                        });
                    }
                }).start();*/
            }
        });

        //Connect button
        connectButton.setOnClickListener(new View.OnClickListener() {
            public void onClick(View v) {
                try {
                    openBT();
                } catch (IOException ex) {
                }
            }
        });

        //Close button
        closeButton.setOnClickListener(new View.OnClickListener() {
            public void onClick(View v) {
                try {
                    closeBT();
                } catch (IOException ex) {
                }
            }
        });

    }

    public void initGraph() {
        entries = new ArrayList<>();
        entries2 = new ArrayList<>();
        entries3 = new ArrayList<>();

        XAxis xAxis = mGraph.getXAxis();
        xAxis.setPosition(XAxis.XAxisPosition.BOTTOM);
        xAxis.setValueFormatter(new IAxisValueFormatter() {
            DecimalFormat mFormat = new DecimalFormat("0");

            @Override
            public String getFormattedValue(float value, AxisBase axis) {
                // "value" represents the position of the label on the axis (x or y)
                return mFormat.format(value);
            }
        });

        YAxis leftAxis = mGraph.getAxisLeft();
        YAxis rightAxis = mGraph.getAxisRight();
        leftAxis.setDrawLabels(true);
        leftAxis.setAxisMinimum(0f);
        leftAxis.setAxisMaximum(maxLevel);
        rightAxis.setDrawLabels(false);
        rightAxis.setAxisMinimum(0f);
        rightAxis.setAxisMaximum(maxLevel);

        mGraph.setDescription(null);
        mGraph.setMaxVisibleValueCount(0);

        LimitLine ll = new LimitLine(criticalLevel, "Critical Level");
        ll.setLineColor(Color.RED);
        ll.setLineWidth(1f);
        ll.setTextColor(Color.BLACK);
        ll.setTextSize(12f);
        leftAxis.addLimitLine(ll);
    }

//    private void updateGraph(float data, float data2) {
private void updateGraph(float data) {
        //Calendar calendar = Calendar.getInstance();
        //String x = calendar.get(Calendar.HOUR_OF_DAY)+":"+calendar.get(Calendar.MINUTE);
        float timeSlot = 1;
        float x = graphCounter * timeSlot;
        float y = data;
//        float y2 = data2;

        if (graphCounter == 0) {
            entries.add(new Entry(x, y));
//            entries2.add(new Entry(x, y2));
//            entries3.add(new Entry(x, (new Random().nextFloat()) * 1f));
            set1 = new LineDataSet(entries, "H2");
            set1.setColor(Color.rgb(57, 106, 177));
            set1.setDrawCircles(false);
            set1.setLineWidth(1.5f);
            set2 = new LineDataSet(entries2, "NoWindow");
            set2.setColor(Color.rgb(218, 124, 48));
            set2.setDrawCircles(false);
            set2.setLineWidth(1.5f);
//                set3 = new LineDataSet(entries3,"Benzene");
//                set3.setColor(Color.rgb(62, 150, 81));
//                set3.setDrawCircles(false);
//                set3.setLineWidth(1.5f);
            dataSets = new ArrayList<>();
            dataSets.add(set1);
            dataSets.add(set2);
//                dataSets.add(set3);
            mLineData = new LineData(dataSets);
            mGraph.setData(mLineData);
            mGraph.invalidate();
        } else {

//            if (graphCounter >= 60) {
//                mLineData.removeEntry((graphCounter - 60f) * 1, 0);
//                mLineData.removeEntry((graphCounter - 60f) * 1, 1);
////                mLineData.removeEntry((graphCounter - 60f) * 1.2f, 2);
//            }
            mLineData.addEntry(new Entry(x, y), 0);
//            mLineData.addEntry(new Entry(x, y2), 1);

//            mLineData.addEntry(new Entry(x, (new Random().nextFloat()) * 1f), 2);
            mGraph.notifyDataSetChanged(); // let the chart know it's data changed
            mGraph.invalidate();
        }
        int progress = Math.round((8500f / (criticalLevel - 5)) * y - ((8500f / (criticalLevel - 5f)) * 5f - 500f));
        if (progress < 0)
            mProgress.setProgress(0);
        else if (progress < 10000)
            mProgress.setProgress(progress);
        else {
            mProgress.setProgress(10000);
        }
        graphCounter++;
        //myRecord.append(counter+": "+Double.toString(data)+"\n");
    }

    public void findBT() {
        mBluetoothManager = (BluetoothManager) getActivity().getSystemService(Context.BLUETOOTH_SERVICE);
        mBluetoothAdapter = mBluetoothManager.getAdapter();
        if (mBluetoothAdapter == null) {
            myLabel.setText("No bluetooth adapter available");
        }

        if (!mBluetoothAdapter.isEnabled()) {
            Intent enableBluetooth = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
            startActivityForResult(enableBluetooth, 0);
        }

        startScan();

        // below is for non-BLE devices
        /*Set<BluetoothDevice> pairedDevices = mBluetoothAdapter.getBondedDevices();
        if(pairedDevices.size() > 0)
        {
            for(BluetoothDevice device : pairedDevices)
            {
                if(device.getName().equals("USR-BLE101"))
                {
                    mmDevice = device;
                    myLabel.setText("Bluetooth Device Found!!");
                    break;
                }
            }
        }*/
    }

    private void startScan() {
        mHandler.postDelayed(stopScanRunnable, 2000);
        mBluetoothAdapter.startLeScan(mLeScanCallback);
    }

    public String toHex(String arg) {
        return String.format("%x", new BigInteger(1, arg.getBytes()));
    }

    private BluetoothAdapter.LeScanCallback mLeScanCallback = new BluetoothAdapter.LeScanCallback() {

        @Override
        public void onLeScan(final BluetoothDevice device, final int rssi,
                             byte[] scanRecord) {
            getActivity().runOnUiThread(new Runnable() {
                public void run() {
                    if (device.getName() != null) {
                        //myRecord.append(toHex(device.getName())+"\n");
                        if (device.getName().equals("A2")) {
                            findDevice = true;
                            mmDevice = device;
                            myLabel.setText("Bluetooth Device Found!!");
                        }
                    }
                }
            });
        }
    };

    private Runnable stopScanRunnable = new Runnable() {
        @Override
        public void run() {
            if (mBluetoothAdapter != null) {
                mBluetoothAdapter.stopLeScan(mLeScanCallback);
            }
            if (mmDevice == null)
                myLabel.setText("No matching device found! Try again!");
        }
    };

    public void openBT() throws IOException {
        /*UUID uuid = UUID.fromString("0003cdd0-0000-1000-8000-00805f9b0131"); //Standard SerialPortService ID
        mmSocket = mmDevice.createRfcommSocketToServiceRecord(uuid);
        mmSocket.connect();
        mmOutputStream = mmSocket.getOutputStream();
        mmInputStream = mmSocket.getInputStream();
        myLabel.setText("Bluetooth Opened");
        myCheckbox.setChecked(true);
        beginListenForData();*/

        if (mmDevice != null) {
            //修改BLE接口最大接收大小 190918 LMT
            mBluetoothGatt = mmDevice.connectGatt(getActivity(), false, mGattCallback);
        } else {
            myLabel.setText("Please connect to device first!");
        }

    }

    private final BluetoothGattCallback mGattCallback = new BluetoothGattCallback() {
        @Override
        public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
            String intentAction;
            if (newState == BluetoothProfile.STATE_CONNECTED) {
//                mBluetoothGatt.discoverServices();
//                /*final Intent intent = new Intent("com.example.bluetooth.le.ACTION_GATT_CONNECTED");
//                getActivity().sendBroadcast(intent);*/
//                Log.i("onConnection: ", "Connected to GATT server.");
//                // Attempts to discover services after successful connection.
//                Log.i("onConnection: ", "Attempting to start service discovery:" +
//                        mBluetoothGatt.discoverServices());
                //改变mtu大小
                if (Build.VERSION.SDK_INT > 20) {
                    boolean ret = gatt.requestMtu(512);
                    Log.d("BLE", "requestMTU " + 512 + " ret=" + ret);
                }

            } else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
                /*final Intent intent = new Intent("com.example.bluetooth.le.ACTION_GATT_DISCONNECTED");
                getActivity().sendBroadcast(intent);*/
            }
        }

        @Override
        public void onMtuChanged(BluetoothGatt gatt, int mtu, int status) {
            super.onMtuChanged(gatt, mtu, status);
            Log.d("onMtuChanged", " onMtuChanged");
            if (BluetoothGatt.GATT_SUCCESS == status) {
                Log.d("BleService", "onMtuChanged success MTU = " + mtu);
                mBluetoothGatt.discoverServices();
                /*final Intent intent = new Intent("com.example.bluetooth.le.ACTION_GATT_CONNECTED");
                getActivity().sendBroadcast(intent);*/
                Log.i("onConnection: ", "Connected to GATT server.");
                // Attempts to discover services after successful connection.
                Log.i("onConnection: ", "Attempting to start service discovery:" +
                        mBluetoothGatt.discoverServices());
            } else {
                Log.d("BleService", "onMtuChanged fail ");
            }
        }


        public void onServicesDiscovered(BluetoothGatt gatt, int status) {
            if (status == BluetoothGatt.GATT_SUCCESS) {
                mCharacteristic = mBluetoothGatt.getService(UUID.fromString("0003cdd0-0000-1000-8000-00805f9b0131")).getCharacteristic(UUID.fromString("0003cdd1-0000-1000-8000-00805f9b0131"));
                Log.i("onServicesDiscovered: ", "found characteristic!");
                mBluetoothGatt.setCharacteristicNotification(mCharacteristic, true);
                getActivity().runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        myLabel.setText("Bluetooth Opened!");
                        myCheckbox.setChecked(true);
                    }
                });
                if (mCharacteristic.getDescriptor(UUID.fromString("00002902-0000-1000-8000-00805f9b34fb")) != null) {
                    mDescriptor = mCharacteristic
                            .getDescriptor(UUID.fromString("00002902-0000-1000-8000-00805f9b34fb"));
                    mDescriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
                    mBluetoothGatt.writeDescriptor(mDescriptor);
                    Log.i("onServicesDiscovered: ", "enabled notification!");
                }

            } else {
                Log.i("onServicesDiscovered: ", Integer.toString(status));
            }
        }

        public void onCharacteristicChanged(BluetoothGatt gatt,
                                            BluetoothGattCharacteristic characteristic) {
            //data processing
            final byte[] data = characteristic.getValue();
            if (data != null && data.length > 0) {
               /* final StringBuilder stringBuilder = new StringBuilder(data.length);
                for (byte byteChar : data)
                    stringBuilder.append(String.format("%02X ", byteChar));*/
//                try{
//                    final String outData = new String(data, "US-ASCII");
                mData.storeData(data);
//                    cycleCounter++;
//                    //190916修改 12循环改为16 LMT
//                    //190917修改 取消循环 LMT
//                    if(cycleCounter == 12){
                if (graphCounter == 0 && baseline == 0) {
                    baseline = dataAverage(mData.getSingleData(sensorNum)) + 2f;
                }

                float pd=mData.getSingleData(sensorNum);
                if (pd<0){
                    pd=saveData[9];
                }
                final float printData = dataAverage(pd);
                final float printData2 =pd;

                getActivity().runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        if (printData <= 0f) {
                            myRecord.setText("Super clean!" + "\n");
//                            updateGraph(printData, printData2);
                            updateGraph(printData);
                        } else if (printData < criticalLevel) {
                            myRecord.setText("Normal" + "\n");
//                            updateGraph(printData, printData2);
                            updateGraph(printData);
                        } else if (printData < maxLevel) {
                            Spannable WordtoSpan = new SpannableString("Warning: town gas detected!");
                            WordtoSpan.setSpan(new ForegroundColorSpan(Color.RED), 0, 27, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
                            myRecord.setText(WordtoSpan);
//                            updateGraph(printData, printData2);
                            updateGraph(printData);
                        } else {
                            Spannable WordtoSpan = new SpannableString("Evacuate immediately!");
                            WordtoSpan.setSpan(new ForegroundColorSpan(Color.RED), 0, 21, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
                            myRecord.setText(WordtoSpan);
//                            updateGraph(printData, printData2);
                            updateGraph(printData);
                        }
                    }
                });
//                        cycleCounter = 1 ;
//                    }

//                } catch (IOException ex) {
//                    stopWorker = true;
//                }
            }
        }

    };

   /* public void beginListenForData() {
        final Handler handler = new Handler();
        final byte delimiter = 10; //This is the ASCII code for a newline character

        stopWorker = false;
        readBufferPosition = 0;
        readBuffer = new byte[1024];
        workerThread = new Thread(new Runnable() {
            public void run() {
                while (!Thread.currentThread().isInterrupted() && !stopWorker) {
                    try {
                        int bytesAvailable = mmInputStream.available();
                        if (bytesAvailable > 0) {
                            byte[] packetBytes = new byte[bytesAvailable];
                            mmInputStream.read(packetBytes);
                            for (int i = 0; i < bytesAvailable; i++) {
                                byte b = packetBytes[i];
                                if (b == delimiter) {
                                    byte[] encodedBytes = new byte[readBufferPosition];
                                    System.arraycopy(readBuffer, 0, encodedBytes, 0, encodedBytes.length);
                                    final String data = new String(encodedBytes, "US-ASCII");
                                    readBufferPosition = 0;

                                    handler.post(new Runnable() {
                                        public void run() {
                                            myRecord.append(data);
                                        }
                                    });
                                } else {
                                    readBuffer[readBufferPosition++] = b;
                                }
                            }
                        }
                    } catch (IOException ex) {
                        stopWorker = true;
                    }
                }
            }
        });

        workerThread.start();
    }*/

    public void closeBT() throws IOException {
        if (mBluetoothGatt == null) {
            return;
        }
        mBluetoothGatt.close();
        mBluetoothGatt = null;
        myLabel.setText("Bluetooth Closed");
        myCheckbox.setChecked(false);
    }

    public void setSensorNum(int sensor) {
        sensorNum = sensor;
    }

    public void setBaseSignal(float base) {
        baseline = base;
    }

    public void setMaxLevel(float max) {
        maxLevel = max;
    }

    public void setCriticalLevel(float critical) {
        criticalLevel = critical;
    }

    public void changeRecord() {
        myRecord.setText("changes");
    }


    //data average
    private static float[] saveData = new float[10];
    private static int dataAvrCount = 0;

    public static float dataAverage(float data) {
        float sumData = 0;
        //adapt savedata
        if (dataAvrCount < 10) {
            saveData[dataAvrCount] = data;
        } else {
            for (int j = 0; j < 9; j++) {
                saveData[j] = saveData[j + 1];
            }
            saveData[9] = data;
        }
        //calculate average
        for (int k = 0; k < 10; k++) {
            sumData = sumData + saveData[k];
        }
        int avaCount = 1;
        if (dataAvrCount < 10) {
            avaCount = dataAvrCount + 1;
        } else {
            avaCount = 10;
        }
        float avaNum = sumData / avaCount;
        dataAvrCount++;
        return avaNum;
    }

}